// 创建订阅器
let uid$1 = 0
class Watcher {
  constructor(vm, key, callback) {
    this.id = ++uid$1
    this.vm = vm
    this.key = key
    this.callback = callback
    Dep.target = this
    this.oldValue = this.vm[this.key]
    Dep.target = null
  }
  notify() {
    const newValue = this.vm[this.key]
    if (this.oldValue === newValue) return
    this.callback(newValue)
    this.oldValue = newValue
  }
}
// 依赖收集器
let uid = 0
class Dep {
  constructor() {
    this.id = ++uid
    this.subs = []
    Dep.subs = this.subs
  }
  // 添加订阅者
  addSub() {
    console.log('添加订阅者');
    Dep.target && this.subs.push(Dep.target)
  }
  notify() {
    console.log('通知订阅者');
    this.subs.forEach(subs => subs.notify())
  }
}
// 观察者
class Observe {
  constructor(vm, data) {
    this.vm = vm
    this.data = data || {}
    // 第一次遍历 data 时，收集依赖关系
    this.walk()
  }
  walk() {
    Object.keys(this.data).forEach(key => {
      this.defineReactive(this.data, key, this.data[key])
    })
  }
  defineReactive(data, key, value) {
    // 每个观察期都有一个依赖收集器实例
    const dep = new Dep()
    // 如果值是对象，递归定义响应式
    if (typeof value === 'object' && value !== null) {
      new Observe({ data: value })
    }
    Object.defineProperty(data, key, {
      // 可枚举
      enumerable: true,
      // 可配置
      configurable: true,
      get() {
        // 获取属性时，添加依赖收集器
        dep.addSub()
        return value
      },
      set(newValue) {
        console.log(newValue);
        if (newValue === value) return
        if (typeof newValue === 'object' && newValue !== null) {
          new Observe({ data: newValue })
        }
        value = newValue
        console.log('通知所有订阅者，数据发生了变化');
        // 通知所有订阅者，数据发生了变化
        dep.notify()
      }
    })
  }
}
// 模版解析器
class Compile {
  constructor(vm, el) {
    this.$vm = vm
    this.$el = el
    // 解析模板
    this.compile()
  }
  compile() {
    const that = this
    const el = this.$el
    // 解析node节点
    function getNode(childNodes) {
      childNodes.forEach(node => {
        if (node.nodeType === 1) {
          getNode(node.childNodes)
        }
        if (node.nodeType === 3) {
          that.compileText(node)
        }
      })
    }
    getNode(el.childNodes)
  }
  // 处理文本模版
  compileText(node) {
    if (node.textContent.trim())  {
        let reg = /{{(.*?)}}/g
        let textContent = node.textContent
        let match
        while (match = reg.exec(textContent)) {
          const text = match[0]
          const key = match[1].trim()
          node.textContent = textContent.replace(text, this.$vm[key])
          // 创建一个文本订阅者
          new Watcher(this.$vm, key, (newValue) => {
            node.textContent = textContent.replace(text, newValue)
          })
        }

    }
  }
}

// vue
class Vue {
  constructor(options) {
    this.$options = options || {}
    this.$data = options.data || {}
    const el = options.el
    this.$el = typeof el === 'string' ? document.querySelector(el) : el
    
    // 1. 将 data 中的属性注入到 this实例 中
    proxy(this, this.$data)
    // 2. 监听 data 中的属性，当属性值发生变化时，更新视图
    new Observe(this, this.$data)
    // 3. 解析模板，将解析后的 dom 插入到页面中
    new Compile(this, this.$el)
  }
}
// 注入
function proxy(target, targetData) {
  Object.keys(targetData).forEach(key => {
    Object.defineProperty(target, key, {
      // 可枚举
      enumerable: true,
      // 可配置
      configurable: true,
      get() {
        return targetData[key]
      },
      set(value) {
        targetData[key] = value
      }
    })
  })
}
